package com.lambo.maven.core.utils.superHttp.utils;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;
import java.util.List;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import javax.xml.bind.DatatypeConverter;

import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.util.TextUtils;

import com.lambo.maven.core.utils.superHttp.core.IOUtils;
import com.lambo.maven.core.utils.superHttp.core.ResourceUtil;

/**
 * 证书构建
 * SSLContextUtil
 */
public class SSLContextUtil {

    public static TrustManager[] buildTrustManager() {    
        X509TrustManager t = new X509TrustManager(){
            @Override
            public void checkClientTrusted(X509Certificate[] arg0,
                    String arg1) throws CertificateException {
            }
            @Override
            public void checkServerTrusted(X509Certificate[] arg0,
                    String arg1) throws CertificateException {
            }
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };
        TrustManager[] ts = new TrustManager[]{t};
        return ts;
    }

    public static SSLConnectionSocketFactory buildSSlConn() {
        SSLContext sslContext;
		try {
			sslContext = SSLContext.getInstance(SSLConnectionSocketFactory.TLS);
		} catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
		}
        SSLConnectionSocketFactory sf = new SSLConnectionSocketFactory(
            sslContext, new HostnameVerifier(){
                @Override
                public boolean verify(String hostname, SSLSession sslSession) {
                    return true;
                }
            });
        return sf;
    }

    public static SSLConnectionSocketFactory buildSSLConn(String pemPath, String password) throws Exception {
        SSLContext sslContext = SSLContext.getInstance(SSLConnectionSocketFactory.TLS);
        
        TrustManager[] trust = buildTrustManager();
        KeyManager[] km = getKeyManagers(pemPath, password);
        sslContext.init(km, trust, null);

        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
            sslContext,  new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession sslSession) {
                    return true;
                }
            });
    
        return sslsf;
    }

    /**
     * 证书构建
     * @param pemPath 用于服务端校验客户端
     * @param password 用户服务端校验客户端
     * @param crtPath 用户客户端信任服务端
     * @return
     * @throws Exception
     */
    public static SSLConnectionSocketFactory buildSSLConn(String pemPath, 
            String password, String crtPath, HostnameVerifier verifier) throws Exception {
        SSLContext sslContext = SSLContext.getInstance(SSLConnectionSocketFactory.TLS);
        
        TrustManager[] trust = getTrustManagers(crtPath);
        KeyManager[] km = getKeyManagers(pemPath, password);
        sslContext.init(km, trust, null);

        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
            sslContext, verifier);
    
        return sslsf;
    }

    public static HostnameVerifier buildHostNameVerifier(boolean verifyHostName) {
        if(verifyHostName) {
            return SSLConnectionSocketFactory.getDefaultHostnameVerifier();
        } else {
            return new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession sslSession) {
                    return true;
                }
            };
        }
    }

    public static final HostnameVerifier buildSecureHostnameVerifier(final String[] hostArray) {
        final List<String> list = Arrays.asList(hostArray);
        return new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                if (TextUtils.isEmpty(hostname)) {
                    return false;
                }
				return list.contains(hostname);
            }
        };
    }

    protected static byte[] parseDERFromPEM(byte[] pem, String beginDelimiter, String endDelimiter) {
        String data = new String(pem);
        String[] tokens = data.split(beginDelimiter);
        tokens = tokens[1].split(endDelimiter);
        return DatatypeConverter.parseBase64Binary(tokens[0]);        
    }
    
    private static RSAPrivateKey generatePrivateKeyFromDER(byte[] keyBytes) throws InvalidKeySpecException, NoSuchAlgorithmException {
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
    
        KeyFactory factory = KeyFactory.getInstance("RSA");
    
        return (RSAPrivateKey)factory.generatePrivate(spec);        
    }
    
    private static X509Certificate generateCertificateFromDER(byte[] certBytes) throws CertificateException {
        CertificateFactory factory;
		try {
			factory = CertificateFactory.getInstance("X.509");
            return (X509Certificate)factory.generateCertificate(new ByteArrayInputStream(certBytes));      

		} catch (java.security.cert.CertificateException e) {
			throw new RuntimeException(e);
		}
    
    }

    public static X509Certificate getX509(String crtPath) throws CertificateException, FileNotFoundException {
        CertificateFactory cAf = CertificateFactory.getInstance("X.509");
        FileInputStream caIn = new FileInputStream(crtPath);
        X509Certificate ca = (X509Certificate) cAf.generateCertificate(caIn);
        return ca;
    }

    private static TrustManager[] getTrustManagers(String crtPath) throws Exception {		
        // CA certificate is used to authenticate server
        CertificateFactory cAf = CertificateFactory.getInstance("X.509");
        InputStream caIn = ResourceUtil.getInputStream(crtPath);
        X509Certificate ca = (X509Certificate) cAf.generateCertificate(caIn);
        KeyStore caKs = KeyStore.getInstance("JKS");
        caKs.load(null, null);
        caKs.setCertificateEntry("ca-certificate", ca);
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
        tmf.init(caKs);    		

        return tmf.getTrustManagers();
    }

    public static X509Certificate getX509fromPem(String pemPath) {
        try {
            File file = new File(pemPath);
            FileInputStream is = new FileInputStream(file);
            byte[] certAndKey = IOUtils.toByteArray(is);
            byte[] certBytes = parseDERFromPEM(certAndKey, "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
            
            X509Certificate cert = generateCertificateFromDER(certBytes); 
            return cert;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    /**
     * 
     * @param pemPath
     * @param password
     * @return 
     */
    private static KeyManager[] getKeyManagers(String pemPath, String password) {
        try {
            InputStream is = ResourceUtil.getInputStream(pemPath);
            byte[] certAndKey = IOUtils.toByteArray(is);
            byte[] certBytes = parseDERFromPEM(certAndKey, "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
            byte[] keyBytes = parseDERFromPEM(certAndKey, "-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----");
            
            X509Certificate cert = generateCertificateFromDER(certBytes);    
            
            RSAPrivateKey key  = generatePrivateKeyFromDER(keyBytes);  
            
            KeyStore keystore = KeyStore.getInstance("JKS");
            keystore.load(null);
            keystore.setCertificateEntry("cert-alias", cert);
            keystore.setKeyEntry("key-alias", key, password.toCharArray(), new Certificate[] {cert});
            
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(keystore, password.toCharArray());
            KeyManager[] km = kmf.getKeyManagers(); 
            return km;
        } catch (Exception e) {
            throw new RuntimeException("getKeyManagers异常", e);
        }

    }
}